package com.tacitknowledge.filters.gzipfilter;

import com.tacitknowledge.filters.GenericFilter;
import com.tacitknowledge.filters.GenericResponseWrapper;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.GZIPOutputStream;
import javax.servlet.FilterChain;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class GZIPFilter extends GenericFilter {
	public static final String ACCEPT_ENCODING = "Accept-Encoding";
	public static final String CONTENT_ENCODING = "Content-Encoding";

	public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		if (this.isEnabled()) {
			response.addHeader("Vary", "Accept-Encoding");
			GenericResponseWrapper wrapper = new GenericResponseWrapper(response);
			chain.doFilter(request, wrapper);
			ServletOutputStream out = response.getOutputStream();
			if (!this.isCached(wrapper) && !this.isIncluded(request) && this.acceptsGzip(request)) {
				byte[] data = wrapper.getData();
				byte[] compressedData = this.compressData(data);
				response.setHeader("Content-Encoding", "gzip");
				response.setContentLength(compressedData.length);
				out.write(compressedData);
				String uri = request.getRequestURI();
				double before = data.length;
				double after = compressedData.length;
				System.out.println(uri+"\t压缩前:"+before+"\t压缩后：" + after+"\t压缩比："+((before-after)/before));
			} else {
				out.write(wrapper.getData());
			}

			out.flush();
			out.close();
		} else {
			chain.doFilter(request, response);
		}

	}

	public void printBanner(ServletContext context) {
		context.log("$Id: GZIPFilter.java,v 1.15 2005/03/12 01:52:29 mike Exp $");
		context.log("\tGZIPFilter.Enabled: " + this.isEnabled());
		context.log("\tGZIPFilter.LogStats: " + this.getBooleanConfigEntry("GZIPFilter.LogStats"));
	}

	protected byte[] compressData(byte[] data) throws IOException {
		ByteArrayOutputStream compressed = new ByteArrayOutputStream();
		GZIPOutputStream gzout = new GZIPOutputStream(compressed);
		gzout.write(data);
		gzout.flush();
		gzout.close();
		if (this.getBooleanConfigEntry("GZIPFilter.LogStats")) {
			StringBuffer stats = new StringBuffer("" + data.length);
			stats.append(" / " + compressed.size());
			stats.append(" / " + (data.length - compressed.size()));
			if (data.length > 0) {
				double doubleRatio = (double) compressed.size() / (double) data.length;
				int ratio = (int) (doubleRatio * 100.0D);
				stats.append(" / " + ratio + "%");
			} else {
				stats.append(" / NaN");
			}

			this.getFilterConfig().getServletContext().log("GZIPFilter: Original / GZip / Saved / Ratio: " + stats);
		}

		return compressed.toByteArray();
	}

	protected boolean isIncluded(ServletRequest request) {
		String uri = (String) request.getAttribute("javax.servlet.include.request_uri");
		return uri != null;
	}

	protected boolean isCached(GenericResponseWrapper wrapper) {
		return wrapper.getData().length <= 0;
	}

	protected boolean isEnabled() {
		return this.getBooleanConfigEntry("GZIPFilter.Enabled");
	}

	protected boolean acceptsGzip(HttpServletRequest request) {
		return this.headerContains(request, "Accept-Encoding", "gzip");
	}
}